package cn.unknowpupil.init.utils.thread;

import cn.hutool.core.date.StopWatch;
import com.google.common.collect.Lists;

import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * Created at 2020/11/10 by w10g <br>
 * Initialize ThreadPoolExecutorDemo ... <br>
 *
 * @author unknowpupil
 * @since 0.0.1
 */
public class ThreadPoolExecutorDemo {
    /**
     * 在日常业务开发中，有时对一些没有关联的业务操作，
     * 如查询多个结果，使用串行调用并不是一个理想的处理方式，
     * 可以优化为使用线程池做并发调用，
     * 这样在一定程度上能提高性能，如下测试demo方法，
     * 使用TimeUnit.SECONDS.sleep(xxx)模拟业务处理时长
     *
     * @return
     * @throws Exception
     */
    public static String getName() throws Exception {
        TimeUnit.SECONDS.sleep(5);

        return "阿三";
    }

    public static int getAge() throws Exception {
        TimeUnit.SECONDS.sleep(15);

        return 18;
    }

    public static String getBirthday() throws Exception {
        TimeUnit.SECONDS.sleep(8);

        return "1001";
    }


    /**
     * 使用线程池
     */
    static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(10, 20, 2000L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>());

    /**
     * 2.1 使用ThreadPoolExecutor的submit方法，
     * 参数为Callable类型的可以有返回值
     * public <T> Future<T> submit(Callable<T> task)
     *
     * @param args
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        //  1、串行测试
        StopWatch watch = new StopWatch();
        watch.start("串行");
        String name = getName();
        int age = getAge();
        String birthday = getBirthday();
        watch.stop();
        System.out.println(name + "-" + age + "-" + birthday + ", 串行 耗时：" + watch.getTotalTimeMillis());
        // 2.并行测试1
        StopWatch watch2 = new StopWatch();
        watch2.start("并行");
        // 此方式get方法阻塞了后面的服务，用法不对
        String submit = poolExecutor.submit(() -> getName()).get();
        Integer submit1 = poolExecutor.submit(() -> getAge()).get();
        String submit2 = poolExecutor.submit(() -> getBirthday()).get();
        watch2.stop();
        System.out.println(submit + "-" + submit1 + "-" + submit2 + ", 并行1 耗时：" + watch2.getTotalTimeMillis());

        // 3.并行测试2
        StopWatch watch3 = new StopWatch();
        watch3.start("并行2");
        Future<String> future = poolExecutor.submit(() -> getName());
        Future<Integer> future1 = poolExecutor.submit(() -> getAge());
        Future<String> future2 = poolExecutor.submit(() -> getBirthday());
        String submit3 = future.get();
        Integer submit4 = future1.get();
        String submit5 = future2.get();
        watch3.stop();
        System.out.println(submit3 + "-" + submit4 + "-" + submit5 + ", 并行3 耗时：" + watch3.getTotalTimeMillis());

        // 4.并行测试3  2.2 对返回值为统一类型的可以使用invokeAll方法：
        //
        //public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
        StopWatch watch4 = new StopWatch();
        watch4.start("并行3");
        Callable<Object> taskName = () -> getName();
        Callable<Object> taskAge = () -> getAge();
        Callable<Object> taskBirthday = () -> getBirthday();
        List<Callable<Object>> taskList = Lists.newArrayList();
        taskList.add(taskAge);
        taskList.add(taskName);
        taskList.add(taskBirthday);
        List<Future<Object>> futures = poolExecutor.invokeAll(taskList);

        StringBuilder sb = new StringBuilder();
        for (Future<Object> resultFuture : futures) {
            sb.append(resultFuture.get()).append("-");
        }
        watch4.stop();
        System.out.println(sb.toString() + ", 并行3 耗时：" + watch4.getTotalTimeMillis());
    }
}
